﻿
namespace Patterns4Net.ArchitectureExplorer.ViewModels
{
    using System.ComponentModel.Composition;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.Xml.Linq;
    using Caliburn.Micro;
    using Mono.Cecil;
    using Patterns4Net.ArchitectureExplorer.Diagram;
    using Patterns4Net.ArchitectureExplorer.Documentation;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Detail;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Messages;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Services;

    [Export(typeof(DetailPanelViewModel))]
    public class DetailPanelViewModel : PropertyChangedBase, IHandle<DiagramElementSelectedMessage>
    {
        private readonly ApiDocProvider apiDocProvider;

        private readonly DocumentationParser docParser;

        private DiagramElement currentElement;

        [ImportingConstructor]
        public DetailPanelViewModel(
            ApiDocProvider apiDocProvider,
            DocumentationParser docParser,
            IEventAggregator eventAggregator)
        {
            this.apiDocProvider = apiDocProvider;
            this.docParser = docParser;
            eventAggregator.Subscribe(this);
        }

        public object Detail { get; private set; }

        public void Handle(DiagramElementSelectedMessage message)
        {
            this.currentElement = message.DiagramElement;
            this.SetUpDetail((dynamic)this.currentElement);
            this.NotifyOfPropertyChange(() => this.Detail);
        }

        private void SetUpDetail(DiagramElement diagramElement)
        {
            Contract.Assert(
                false,
                string.Format("DetailPanelViewModel cannot handle selected element of type {0}", diagramElement.GetType()));
        }

        private void SetUpDetail(Package package)
        {
            this.Detail = new PackageDetailViewModel
                {
                    Name = package.Name,
                    TypesCount = package.Types.Count(),
                    MethodsCount = package.Types.SelectMany(t => t.Methods).Count(),
                    PropertiesCount = package.Types.SelectMany(t => t.Properties).Count(),
                    PatternsCount = package.Types.SelectMany(t => t.Patterns).Count() +
                        package.Types.SelectMany(t => t.Methods).SelectMany(m => m.Patterns).Count(),
                };
        }

        private void SetUpDetail(Layer layer)
        {
            this.Detail = new LayerDetailViewModel
                {
                    Name = layer.Name,
                    Packages = layer.Packages.Select(x => x.Name),
                };
        }

        private void SetUpDetail(Diagram diagram)
        {
            this.Detail = new DiagramDetailViewModel { Layers = diagram.Layers.Select(x => x.Name) };
        }

        private void SetUpDetail(TypeInfo type)
        {
            var detail = new TypeDetailViewModel
                {
                    Name = type.Name,
                    Package = type.Parent.Name,
                    Layer = type.Parent.Parent.Name,
                    Patterns = type.Patterns.Select(x => x.Pattern.Name),
                };
            this.Detail = detail;

            var typeDefinition = type.GetTag<TypeDefinition>();
            if (typeDefinition != null)
            {
                detail.Namespace = typeDefinition.Namespace;

                XElement element;
                detail.ApiDocSearchResult = this.apiDocProvider.GetDocumentation(typeDefinition, out element);
                if (element != null)
                {
                    detail.Documentation = this.docParser.Transform(element);
                }
            }            
        }
    }
}
